﻿using System;
using System.Drawing;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Truncation.Detector.WinForm.Controls
{
    public abstract class BaseControl
    {
        public abstract bool Measure();
        protected static bool Measure(Font font, string txt, int clientWidth, int clientHeight)
        {
            IntPtr hWnd = IntPtr.Zero;
            IntPtr hdc = GetDC(hWnd);
            IntPtr hGDIObj = font.ToHfont();
            IntPtr handle = SelectObject(hdc, hGDIObj);
            TextElementEnumerator charEnum = StringInfo.GetTextElementEnumerator(txt);
            int total = 0;
            int fontHeight = TextRenderer.MeasureText(txt, font).Height;
            if (clientHeight < fontHeight)
                return false;
            while (clientHeight > 0 && charEnum.MoveNext())
            {
                string textElement = charEnum.GetTextElement();

                //int unicode = textElement.Length == 2 ? Char.ConvertToUtf32(textElement[0], textElement[1]) : Char.ConvertToUtf32(textElement, 0);

                int unicode = textElement.Length == 2 ? (textElement[0] << 16 | textElement[1]) : textElement[0];

                int width = 0;
                if (unicode < 32 && unicode > 0)
                {
                    if (unicode == 0x0a)
                    {
                        clientHeight -= font.Height;
                    }
                }
                else if (textElement.Length == 1)
                {
                    ABC[] glyphSet = new ABC[1];
                    GetCharABCWidthsW(hdc, (uint)unicode, (uint)unicode, glyphSet);
                    width = glyphSet[0].abcA + (int)glyphSet[0].abcB + glyphSet[0].abcC;
                }
                else
                {
                    var bmp = new Bitmap(100, 100);
                    var g = Graphics.FromImage(bmp);
                    width = TextWidth(g, font, textElement);
                    g.Dispose();
                    bmp.Dispose();
                }
                total += width;
                if (total > clientWidth)
                {
                    total = width;
                    clientHeight -= font.Height;
                }
            }
            if (clientHeight <= 0)
            {
                return false;
            }
            return true;
        }
        private static int TextWidth(Graphics g, Font font, string txt)
        {
            StringFormat format = new StringFormat(StringFormatFlags.MeasureTrailingSpaces | StringFormatFlags.NoWrap);
            RectangleF rect = new RectangleF(0, 0, 1000, 1000);
            CharacterRange[] ranges = { new CharacterRange(0, txt.Length) };
            Region[] regions = new Region[1];
            format.SetMeasurableCharacterRanges(ranges);
            regions = g.MeasureCharacterRanges(txt, font, rect, format);
            var w1 = regions[0].GetBounds(g).Width;
            string ruler = new string(txt[txt.Length - 1], txt.Length);
            regions = g.MeasureCharacterRanges(ruler, font, rect, format);
            var w2 = regions[0].GetBounds(g).Width;
            if (w1 > w2)
            {
                return (int)w1 + 1;
            }
            return (int)w1;
        }


        [StructLayout(LayoutKind.Sequential)]
        private struct ABC
        {
            public int abcA;
            public uint abcB;
            public int abcC;
        }

        [DllImport("gdi32.dll")]
        private static extern int GetCharABCWidthsW(IntPtr hdc, uint start, uint end, [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStruct)] ABC[] lpABC);

        [DllImport("gdi32.dll")]
        private static extern int DeleteObject(IntPtr hGdiObj);

        [DllImport("user32.dll")]
        private static extern IntPtr GetDC(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

        [DllImport("gdi32.dll")]
        private static extern IntPtr SelectObject(IntPtr hDC, IntPtr hGdiObj);
    }
}